package com.tutou.ikuser.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tutou.ikuser.filter.JwtAuthenticationFilter;
import com.tutou.ikuser.mapper.UserMapper;
import com.tutou.ikuser.pojo.dto.LRegisterDTO;
import com.tutou.ikuser.pojo.dto.Result;
import com.tutou.ikuser.pojo.dto.UserDTO;
import com.tutou.ikuser.pojo.po.User;
import com.tutou.ikuser.pojo.vo.RedisUser;
import com.tutou.ikuser.service.UserService;
import com.tutou.ikuser.util.JwtUtil;
import com.tutou.ikuser.util.ValidateCodeUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.tutou.ikuser.util.RedisConstants.*;


@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void checkCode(HttpServletRequest request, HttpServletResponse response) {
        response.setContentType("image/png");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Expire", "0");
        response.setHeader("Pragma", "no-cache");
        ValidateCodeUtil validateCode = new ValidateCodeUtil();
        // getRandomCodeImage方法会直接将生成的验证码图片写入response
        validateCode.getRandomCodeImage(request, response);
        //System.out.println("session里面存储的验证码为：" + session.getAttribute("JCCODE"));
        //将验证码存入redis中并设置过期时间
        HttpSession session = request.getSession();
        String id = session.getId();
        String key = (String) session.getAttribute("JCCODE");
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + id, key, LOGIN_CODE_TTL, TimeUnit.MINUTES);
    }

    @SneakyThrows
    @Override
    public Result login(LRegisterDTO lregisterDTO, HttpServletRequest request) {
        HttpSession session = request.getSession();
        String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + session.getId());
        //获取生成的验证码
        try {
            if (!lregisterDTO.getCheckCode().equalsIgnoreCase(code)) {
                return Result.fail("验证码错误~");
            }
        } catch (Exception e) {
            return Result.fail("请输入验证码~");
        }
        //通过UsernamePasswordAuthenticationToken封装电话和密码,封装成Authentication对象后给AuthenticationManager进行认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken
                (lregisterDTO.getPhone(), lregisterDTO.getPassword());
        //进行用户认证,用authenticationToken和在userDetails返回的UserDetails（重写的SecurityUser）作比较
        //如果一致，则会将UserDetails设置的权限信息设置到Authentication对象中
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (Objects.isNull(authenticate)) {
            return Result.fail("请检查电话以及密码是否错误或注册账号~");
        }
        User user = parseSecurityUser(authenticate.getPrincipal().toString());
        //将User对象转化为HashMap对象
        Map<String, Object> userMap = BeanUtil.beanToMap(user, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        //认证通过，生成JWT
        String token = JwtUtil.buildToken(user);
        //jwt作为键存入redis，用于自己获取自己信息，值为该用户的所有信息
        String userToken = LOGIN_TOKEN_KEY + token;
        stringRedisTemplate.opsForHash().putAll(userToken, userMap);
        stringRedisTemplate.expire(userToken, LOGIN_TOKEN_TTL, TimeUnit.MINUTES);
        //电话号码作为键存入redis，用于判断用户是否存在，值只有用户的用户名和id
        stringRedisTemplate.opsForValue().set(USER_EXIST_KEY + lregisterDTO.getPhone(),
                new ObjectMapper().writeValueAsString(new RedisUser(user)));
        stringRedisTemplate.expire(USER_EXIST_KEY + lregisterDTO.getPhone(), LOGIN_TOKEN_TTL, TimeUnit.MINUTES);
        return Result.ok(token);
    }

    @Override
    public Result register(UserDTO userDTO) {
        //对用户密码交给springSecurity进行加密
        String encodedPassword = passwordEncoder.encode(userDTO.getPassword());
        userDTO.setPassword(encodedPassword);
        userMapper.register(userDTO);
        return Result.ok("注册成功~");
    }

    @Override
    public Result logout(String token) {
        //删除用户token信息
        stringRedisTemplate.delete(token);
        //清除SecurityContextHolder认证信息
        SecurityContextHolder.clearContext();
        return Result.ok("已退出~");
    }

    //将authenticate.getPrincipal()里面的用户信息转为User对象
    public User parseSecurityUser(String securityUserString) {
        // 截取 User 对象的部分字符串
        // 定义正则表达式
        String regex = "User\\(userId=(\\d+), userName=([^,]+), password=([^,]+), avatarUrl=([^,]+), phone=(null|[^)]*)\\)";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(securityUserString);

        // 分割字符串并提取字段值
        if (matcher.find()) {
            String userId = matcher.group(1);
            String userName = matcher.group(2);
            String password = matcher.group(3);
            String avatarUrl = matcher.group(4);
            //String phone = matcher.group(5).equals("null") ? null : matcher.group(5);
            String phone = matcher.group(5);
            // 创建 User 对象
            User user = new User(userId, userName, password, avatarUrl, phone);
            return user;
        }
        return null;
    }
}
